#define _BSD_SOURCE 1
#define _XOPEN_SOURCE_EXTENDED 1
#include "Service.h"
#include "User.h"
#include "Directory.h"
#include "File.h"
#include "RequestHeader.h"
#include "RequestParameter.h"
#include "ResponseHeader.h"
#include "DAO.h"
#include "HashMap.h"
#include "Global.h"
#include "FileManager.h"
#include "PublicUtils.h"
#include "Token.h"
#include <header.h>


/* getfile */
int userDownloadFileService(int net_fd, RequestHeader *header, MYSQL *conn){
    /**
     * 接收参数链表
     * 参数一：token
     * 参数二：filename
     * 参数三：current directory
     * 
     * 验证token
     *  过期返回0，重新登录
     * 
     * 查文件仓库，有无此文件
     * 
     *      若有，根据偏移量发送
     * 
     *      若无，ret 0
    */
    RequestParameter *param =  recvLinkedList(net_fd, header->argcnt);
    char *token = param->argv;
    char *fileName = param->next->argv;
    char *currentDir = param->next->next->argv;

    int uid = token_decode(token);    
    if(uid < 0){
        /* token过期或无效 */
        freeLinkedList(param);
        sendResponseHeader(net_fd, 0, "token invalid");
        return 0;
    }

    File *file = isUserHaveThisFileDAO(conn, fileName, currentDir);
    if(file == NULL){
        // printf("2no\n");
        /* 无此文件 */
        freeLinkedList(param);
        sendResponseHeader(net_fd, -1, "no such file");
        return 0;        
    }

    /* centos7中无itoa() */
    char fileSize2String[20];
    sprintf(fileSize2String, "%d", file->filesize);
    sendResponseHeader(net_fd, 1, fileSize2String);
    
    char path[BUFF_SIZE];
    snprintf(path, sizeof(path) - 1, "%s/%s", "root", file->filemd5);
    int fd = open(path, O_RDWR);
    /* 设置偏移量 */
    lseek(fd, header->offset, SEEK_SET);

    sendfile(net_fd, fd, NULL, file->filesize - header->offset);
    

    close(fd);
    return 0;
} 

/* putfile */
int userUploadFileService(int net_fd, RequestHeader *header, MYSQL *conn){
    /**
     * 接收参数链表
     * 参数一：token
     * 参数二：md5
     * 参数三：用户上传文件的目录
     * 参数四：文件名（同一文件可能不同名字）
     * 
     * 验证token
     *  过期返回0，重新登录
     * 
     * 查文件仓库，有无同md5值文件
     * 
     * 若有，秒传，加记录到user_file表，return 1，客户端不用发送文件
     * 
     * 若无，接收文件，插入记录到file表，user_file表
    */
    RequestParameter *param =  recvLinkedList(net_fd, header->argcnt);
    char *token = param->argv;
    char *md5 = param->next->argv;
    char *currentDir = param->next->next->argv;
    char *fileName = param->next->next->next->argv;

    int uid = token_decode(token);

    // TODO 同一目录下有相同文件则不可上传
    
    if(uid < 0){
        /* token过期或无效 */
        freeLinkedList(param);
        sendResponseHeader(net_fd, 0, "token invalid");
        return 0;
    }

    // 查文件仓库有无同名
    char path[BUFF_SIZE];   // 真实的存储路径
    memset(path, 0, sizeof path);
    strcpy(path, "root/");
    strcat(path, md5);
    int ret = isExistThisFileOrDirectory(path);
    

    if(ret == 0){
        /* 有此文件则插入分享表和文件表,不用接收文件 */
        insertIntoFileAndShareDAO(conn, uid, fileName, header->filesize, md5, currentDir);

        // send respons 1
        sendResponseHeader(net_fd, 1, "file upload success");
        return 1;
    }

    /* 需要发一个响应，可以开始传文件了 */
    sendResponseHeader(net_fd, 2, "begin to uploadfile\n");

    // 无重复，可以接收文件了
    int fildDescriptor = open(path, O_RDWR | O_CREAT, 0600);

    ftruncate(fildDescriptor, header->filesize);
    void *p = mmap(NULL, header->filesize, PROT_READ | PROT_WRITE, MAP_SHARED, fildDescriptor, 0);
    ret = recvn(net_fd, p, header->filesize);

    if(ret == 0){
        // 对方断线需要删除传了一半的文件
        close(fildDescriptor);
        deleteFile(path);
        freeLinkedList(param);
        munmap(p, header->filesize);
        return 0;
    }

    munmap(p, header->filesize);

    insertIntoFileAndShareDAO(conn, uid, fileName, header->filesize, md5, currentDir);


    close(fildDescriptor);
    freeLinkedList(param);
    return 1;
}